interface ICallback {
  (data: any): void;
}

class Socket {
  HEART_CHECK_TIME = 60000; // 心跳检测间隔时间
  HEART_CHECK_END_TIME = 5000; // 心跳检测接受服务器信号时间
  heartCheckSuccess = false; // 心跳检测是否成功

  RECONNECT_TIME = 30000; // 断线重连间隔时间
  MAX_RECONNECT_COUNT = 5; // 最大重连次数
  reconnect_count = 0; // 当前重连次数
  reconnectTimeId: NodeJS.Timeout | undefined;

  callbacks: ICallback[] = [];

  url: string;
  ws: WebSocket;
  constructor(url: string) {
    this.url = url;
    this.ws = this.initWebSocket();
  }

  /*
    检测ws是否出现问题：
      1. 断开链接，触发close事件，在close事件重连
      2. 弱网、断网，不会触发事件，此时使用心跳检测来检查
        客户端每隔1分钟向服务器发送请求特定消息：ping
        服务器接受到这个消息后，需要立即返回一个特定消息：pong
        客户端检测是否在规定时间内（5s）接受到pong消息。
          接受到了，连接是正常的，开始下一次的心跳检测
          接受不到，连接出现问题，重连
  */

  initWebSocket() {
    // 1. 连接ws
    const ws = new WebSocket(this.url);
    // 2. 绑定事件
    // 监听ws是否连接成功
    ws.onopen = this.handleOpen.bind(this);
    // 监听ws是否断开链接
    ws.onclose = this.handleClose.bind(this);
    // 监听ws是否连接失败
    // ws.onerror = this.handleError;
    ws.addEventListener("message", (e) => {
      if (e.data === "pong") return;
      const data = JSON.parse(e.data);
      this.callbacks.forEach((cb) => cb(data));
    });
    return ws;
  }

  handleOpen() {
    // 链接成功，开始心跳检测
    this.startHeartCheck();
    // 重连次数清0
    this.reconnect_count = 0;
  }
  handleClose() {
    // 开始重连
    this.reconnectWebSocket();
  }
  // 接受消息
  onMessage(cb: ICallback) {
    this.callbacks.push(cb);
  }
  // 删除消息
  removeMessage(cb?: ICallback) {
    if (!cb) {
      this.callbacks = [];
      return;
    }
    this.callbacks = this.callbacks.filter((callback) => callback !== cb);
  }
  // handleError(e) {
  //   console.log("error", e);
  // }

  // 心跳检测
  startHeartCheck() {
    this.ws.addEventListener("message", this.getPong);
    this.setHeartCheckTimeout();
  }
  getPong = (e: MessageEvent) => {
    if (e.data === "pong") {
      this.heartCheckSuccess = true;
    }
  };
  setHeartCheckTimeout() {
    setTimeout(() => {
      // 向服务器发送一个特定消息：ping
      this.ws.send("ping");
      // 在规定时间内，检测是否接受到服务器响应的pong信号
      setTimeout(() => {
        if (this.heartCheckSuccess) {
          // 心跳检测成功
          this.heartCheckSuccess = false;
          // 开始下一轮心跳检测
          this.setHeartCheckTimeout();
        } else {
          // 心跳检测失败
          this.ws.close(); // 关闭连接
          // 开始重连
          this.reconnectWebSocket();
        }
      }, this.HEART_CHECK_END_TIME);
    }, this.HEART_CHECK_TIME);
  }

  // 断线重连
  // 1. 每隔一段时间重连一次
  // 2. 重连有最大次数限制
  reconnectWebSocket() {
    if (this.reconnect_count >= this.MAX_RECONNECT_COUNT) {
      alert("重连多次失败，请刷新浏览器试试");
      return;
    }
    // 同时触发多次重连，只让最后一次生效
    clearTimeout(this.reconnectTimeId);
    this.reconnectTimeId = setTimeout(() => {
      this.reconnect_count++;
      this.ws = this.initWebSocket();
    }, this.RECONNECT_TIME);
  }
}

const useWebSocket = (url: string) => {
  return new Socket(url);
};

export default useWebSocket;
